﻿using System;
using RayDen.Library.Core.Primitives;

namespace RayDen.Library.Components.Surface
{
    [Serializable]
    public class MetalMaterial : SurfaceBsdf
    {
        public override BrdfType Type
        {
            get { return BrdfType.Glossy; }
        }

        public MetalMaterial(RgbSpectrum refl, float exp, bool reflSpecularBounce)
        {
            Kr = refl;
            exponent = 1f / (exp + 1f);
            reflectionSpecularBounce = reflSpecularBounce;
        }

        public override bool IsDiffuse() { return false; }
        public override bool IsSpecular() { return true; }

        public override void f(ref Vector wo, ref Vector wi, ref Normal N, ref RgbSpectrum in_fs, out RgbSpectrum fs)
        {
            //throw new NotSupportedException();
            fs = RgbSpectrum.UnitSpectrum();
        }
        //public override void Sample_f(ref Vector wo, out Vector wi, ref Normal N, ref Normal shadeN, float u0, float u1, float u2, ref RgbSpectrum col, out BsdfSampleData result1)
        //{
        //    RgbSpectrum temp = Kr;
        //    Kr = col;
        //    this.Sample_f(ref wo, ref N, ref shadeN, u0, u1, u2, ref hitInfo.TextureData, out result1);
        //    Kr = temp;
        //    wi = result1.Wi;
        //}

        public override void Sample_f(ref Vector wo, ref Normal N, ref Normal shadeN, ref RgbSpectrum in_f, float u0, float u1, float u2, ref SurfaceTextureData surfaceData, out BsdfSampleData result)
        {
            if (surfaceData != null)
            {
                exponent = 1f/(surfaceData.Exponent + 1f);
            }

            result = new BsdfSampleData();
            result.Wi = GlossyReflection(ref wo, exponent, ref shadeN, u0, u1);
            result.Type = this.Type;

            if (Vector.Dot(ref result.Wi, ref shadeN) > 0f)
            {
                result.Type = reflectionSpecularBounce ? BrdfType.Specular : BrdfType.Diffuse;
                result.Pdf = 1f;
                result.F = surfaceData != null ? RgbSpectrum.Max(ref surfaceData.Specular, ref Kr) : Kr;
            }
            else
            {
                result.F = new RgbSpectrum(0f);
                result.Pdf = 0f;
                result.Type = BrdfType.Diffuse;
            }
            /*
            float phi = 2f * MathLab.M_PI * u0;
            float cosTheta = (float)Math.Pow(1f - u1, exponent);
            float sinTheta = MathLab.Sqrt(1f - cosTheta * cosTheta);
            float x = (float)Math.Cos(phi) * sinTheta;
            float y = (float)Math.Sin(phi) * sinTheta;
            float z = cosTheta;

            Vector dir = -wo;
            float dp = (Normal.Dot(ref shadeN, ref dir));
            Vector w = dir - (2f * dp) * (shadeN.ToVec());

            Vector u;
            if (Math.Abs(shadeN.x) > .1f) {
                Vector a = new Vector(0f, 1f, 0f);
                u = (a ^ w);
            }
            else {
                Vector a = new Vector(1f, 0f, 0f);
                u = (a ^ w);
            }
            u.Normalize();
            Vector v = (w ^ u);

            wi = x * u + y * v + z * w;

            if ((wi & shadeN.ToVec()) > 0f) {
                specularBounce = reflectionSpecularBounce;
                pdf = 1f;

                return Kr;
            }
            else {
                pdf = 0f;
                specularBounce = reflectionSpecularBounce;//!!
                return new RgbSpectrum(0f);
            }*/
        }

        public RgbSpectrum GetKr() { return Kr; }

        private RgbSpectrum Kr;
        public float exponent;
        public bool reflectionSpecularBounce;
        public override float Pdf(ref Vector wo, ref Vector wi, BxDFTypes bxDFType)
        {
            return 1f;
        }
        public bool IsSpecularBounce { get { return reflectionSpecularBounce; } }
    };
}